/*
 *
 * Copyright (C) 2012 Walter Lütgenau
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */
package offset.nodes.server.servlet.book.provider;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import offset.nodes.Constants;
import offset.nodes.client.dialog.io.model.BookType;

/**
 * This class retrieves the HTML content from passed URLs using the RequestDispatcher
 *
 * @author Walter L�tgenau
 */
public class PageProvider {

    HttpServlet servlet;
    HttpServletRequest request;
    HttpServletResponse response;
    RequestAttributes attributes;
    BookType bookType;

    /**
     * Used to reset the attributes after a forward
     */
    class RequestAttributes {
        HttpServletRequest request;
        HashMap<String, Object> attributes = new HashMap<String, Object>();

        public RequestAttributes(HttpServletRequest request) {
            this.request = request;
        }

        protected void init() {
            Enumeration attributeNames = request.getAttributeNames();
            while (attributeNames.hasMoreElements()) {
                String attributeName = (String) attributeNames.nextElement();
                attributes.put(attributeName, request.getAttribute(attributeName));
            }
        }

        public void reset() {
           Enumeration attributeNames = request.getAttributeNames();
           ArrayList<String> attributeNameList = new ArrayList<String>();
           while (attributeNames.hasMoreElements()) {
                String attributeName = (String) attributeNames.nextElement();
                attributeNameList.add(attributeName);
            }
            
            for (String attributeName : attributeNameList) {
                request.removeAttribute(attributeName);
            }

           Iterator<String> attributeKeys = attributes.keySet().iterator();
           while (attributeKeys.hasNext()) {
               String attributeKey = attributeKeys.next();
               request.setAttribute(attributeKey, attributes.get(attributeKey));
           }
        }
    }

    public PageProvider(HttpServlet servlet, HttpServletRequest request, HttpServletResponse response) {
        this.servlet = servlet;
        this.request = request;
        this.response = response;
        this.attributes = new RequestAttributes(request);
        attributes.init();
    }

    public String getHtmlByPath(String path) throws ServletException, IOException {
        RequestDispatcher page = getServlet().getServletContext().getRequestDispatcher(Constants.PATH_SEPARATOR + Constants.SERVLETMAPPING_REPOSITORY + path);
        return getHtml(page);
    }

    public String getHtmlByUrl(String urlString) throws ServletException, IOException {
        RequestDispatcher page = getServlet().getServletContext().getRequestDispatcher(getForward(urlString));

        return getHtml(page);
    }

    public ResourceInfo getResourceByPath(String path) throws ServletException, IOException {
        String prefix = Constants.PATH_SEPARATOR + Constants.SERVLETMAPPING_REPOSITORY;
        if (!path.startsWith(prefix))
            path = prefix + path;
        
        RequestDispatcher page = getServlet().getServletContext().getRequestDispatcher(path);

        return getResource(page);
    }

    public ResourceInfo getResourceByUrl(String urlString) throws ServletException, IOException {
        if (!isFullUrl(urlString))
            return getResourceByPath(urlString);
        
        RequestDispatcher page = getServlet().getServletContext().getRequestDispatcher(getForward(urlString));

        return getResource(page);
    }

    public ResourceInfo getResource(RequestDispatcher page) throws ServletException, IOException {
        StreamResponseWrapper wrappedResponse = new StreamResponseWrapper(getResponse());

        UserAgentRequestWrapper wrappedRequest;
        if (bookType != null)
            wrappedRequest = new UserAgentRequestWrapper(bookType, getRequest());
        else
            wrappedRequest = new UserAgentRequestWrapper(getRequest());
        
        page.forward(wrappedRequest, wrappedResponse);
        attributes.reset();

        ResourceInfo result = new ResourceInfo(wrappedResponse.toByteArray(), wrappedResponse.getContentType());

        return result;
    }

    public String getHtml(RequestDispatcher page) throws ServletException, IOException {
        StreamResponseWrapper wrappedResponse = new StreamResponseWrapper(getResponse());

        page.forward(getRequest(), wrappedResponse);
        attributes.reset();

        return wrappedResponse.toString();
    }

    /**
     * Is this a full URL?
     * 
     * @param urlString
     * @return
     */
    protected boolean isFullUrl(String urlString) {
        URL url = null;
        try {
            url = new URL(urlString);
        } catch (MalformedURLException mue) {
            return false; // uncomplete path.
        }

        return true;
    }

    /**
     * From the full URL, just retrieve the part of the path below the
     * context plus the query, if there is any.
     * 
     * @param urlString
     * @return
     * @throws MalformedURLException
     */
    protected String getForward(String urlString) throws MalformedURLException {
        if (urlString.indexOf("?") >= 0)
            return getForwardFromQuery(urlString);

        URL url = null;
        try {
                url = new URL(urlString);
        } catch (MalformedURLException mue) {
            return urlString; // uncomplete path. Should use different method ...ByPath
        }

        String query = null;
        if (url.getQuery() != null)
            query = url.getQuery();

        String[] pathArray = url.getPath().split(Constants.PATH_SEPARATOR);
        if (pathArray.length <= 2)
            return url.getPath() + query;

        StringBuffer path = new StringBuffer(Constants.PATH_SEPARATOR);
        for (int i = 2; i < pathArray.length; i++) {
            path.append(pathArray[i]);
            if (i + 1 < pathArray.length)
                path.append(Constants.PATH_SEPARATOR);
        }

        if (query == null)
            return path.toString();
        
        return path + "?" + query;
    }

    public void setBookType(BookType bookType) {
        this.bookType = bookType;
    }

    /**
     * If there is a query, it must be to the repository servlet. Simplify
     * and possibly correct the url.
     * 
     * @param urlString
     * @return
     */
    protected String getForwardFromQuery(String urlString) {
        String query = urlString.substring(urlString.indexOf("?"));
        //query = addPrintMode(query);
        return Constants.PATH_SEPARATOR + Constants.SERVLETMAPPING_REPOSITORY + query;
    }
    
    protected String addPrintMode(String query) {
        if (query.indexOf(Constants.PAR_MODE) >= 0)
            return query;
        
        return query + Constants.QUERY_PARAMETER_SEPARATOR + Constants.PAR_MODE + Constants.QUERY_ASSIGNEMENT_OPERATOR + Constants.PARVALUE_PRINT;
    }

    public HttpServletRequest getRequest() {
        return request;
    }

    public HttpServletResponse getResponse() {
        return response;
    }

    public HttpServlet getServlet() {
        return servlet;
    }
}
